package cn.com.cetccst.illegalconnection;

import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Handler;
import android.os.Message;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.lang.reflect.Type;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.net.SocketFactory;

public class IllegalConnectionEngine {
    private static final Type arrayType = new TypeToken<Address[]>() {
    }.getType();
    private static IllegalConnectionEngine instance;
    private final Context context;
    private final int detectTimeoutSeconds;
    private NetworkReceiver receiver;
    private final AtomicBoolean isDetecting = new AtomicBoolean(false);
    private final Executor singleExecutor = Executors.newSingleThreadExecutor();

    @SuppressLint("HandlerLeak")
    private final Handler filterHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (!isDetecting.get()) {
                performDetect();
            }
        }
    };

    public static void init(Context context, int detectTimeoutSeconds) {
        if (instance == null) {
            instance = new IllegalConnectionEngine(context, detectTimeoutSeconds);
        }
    }

    public static IllegalConnectionEngine getInstance() {
        return instance;
    }

    private IllegalConnectionEngine(Context context, int detectTimeoutSeconds) {
        this.context = context;
        this.detectTimeoutSeconds = detectTimeoutSeconds;
        new File(context.getFilesDir().getAbsolutePath() + "/IllegalConnection").mkdirs();
        if (loadParams() != null) {
            receiver = new NetworkReceiver();
        }
    }

    public void setParams(List<Address> addressList) {
        synchronized (this) {
            String cfgFile = context.getFilesDir().getAbsolutePath() + "/IllegalConnection/cfg";
            if (addressList == null || addressList.isEmpty()) {
                new File(cfgFile).delete();
                if (receiver != null) {
                    receiver.stop();
                    receiver = null;
                }
                return;
            }
            try (FileOutputStream fileOutputStream = new FileOutputStream(cfgFile)) {
                String str = new Gson().toJson(addressList);
                fileOutputStream.write(str.getBytes());
                if (receiver == null) {
                    receiver = new NetworkReceiver();
                } else {
                    handleNetworkStateChanged();
                }
            } catch (Exception ignored) {
            }
        }
    }

    public Address[] getParams() {
        return loadParams();
    }

    private Address[] loadParams() {
        synchronized (this) {
            String cfgFile = context.getFilesDir().getAbsolutePath() + "/IllegalConnection/cfg";
            try (FileInputStream fileInputStream = new FileInputStream(cfgFile)) {
                byte[] data = new byte[fileInputStream.available()];
                if (data.length > 0) {
                    fileInputStream.read(data);
                    String str = new String(data);
                    return new Gson().fromJson(str, arrayType);
                }
            } catch (Exception ignored) {
            }
            return null;
        }
    }

    public void setLogger(Logger.ILogger logger) {
        Logger.setILogger(logger);
    }

    private void handleNetworkStateChanged() {
        if (isNetworkConnected()) {
            filterHandler.sendEmptyMessageDelayed(1000, 1000);
        } else {
            filterHandler.removeMessages(1000);
        }
    }

    private void performDetect() {
        if (isNetworkConnected()) {
            isDetecting.set(true);
            singleExecutor.execute(this::detect);
        }
    }

    private void detect() {
        Address[] addresses = loadParams();
        if (addresses != null) {
            for (Address address : addresses) {
                if (isAddressReachable(address.getIp(), address.getPort(), detectTimeoutSeconds)) {
                    Logger.log(address.getIp() + " is reachable");
                }
            }
        }
        isDetecting.set(false);
    }

    private boolean isAddressReachable(String host, String port, int timeoutSecond) {
        try {
            Socket socket = SocketFactory.getDefault().createSocket();
            socket.connect(new InetSocketAddress(host, Integer.parseInt(port)), timeoutSecond * 1000);
            socket.close();
            return true;
        } catch (Exception ignored) {
        }
        return false;
    }

    private boolean isNetworkConnected() {
        ConnectivityManager manager = context.getSystemService(ConnectivityManager.class);
        if (manager != null) {
            NetworkInfo info = manager.getActiveNetworkInfo();
            return (info != null && info.isConnected());
        }
        return false;
    }

    private class NetworkReceiver extends BroadcastReceiver {
        public NetworkReceiver() {
            context.registerReceiver(this, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
        }

        public void stop() {
            context.unregisterReceiver(this);
        }

        @Override
        public void onReceive(Context context, Intent intent) {
            handleNetworkStateChanged();
        }
    }
}
